package com.xbongbong.saas.service.toolbox.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.enums.fund.enums.FundSetMenuEnum;
import com.xbongbong.pro.enums.fund.enums.ManageTypeEnum;
import com.xbongbong.pro.enums.fund.enums.ModelTypeEnum;
import com.xbongbong.pro.enums.fund.enums.PeriodTypeEnum;
import com.xbongbong.pro.fundset.pojo.ExplainOpenCloseAttrPojo;
import com.xbongbong.pro.fundset.pojo.FundSetHandleExplainPojo;
import com.xbongbong.pro.fundset.pojo.FundSetMenuPojo;
import com.xbongbong.pro.fundset.pojo.FundSetPeriodFilterPojo;
import com.xbongbong.pro.fundset.pojo.FundSetPeriodRulePojo;
import com.xbongbong.pro.fundset.pojo.PeriodFilterPojo;
import com.xbongbong.paas.field.ReceivablePeriodPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.fund.FundSetConstant;
import com.xbongbong.saas.domain.entity.FundSetEntity;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.domain.entity.ext.CustomerEntityExt;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.dictionary.DayEnum;
import com.xbongbong.saas.model.ContractModel;
import com.xbongbong.saas.model.CustomerModel;
import com.xbongbong.saas.model.FundSetModel;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 资金设置相关公共方法
 * @author zcp
 * @version v1.0
 * @since v1.0
 */
@Component
public class FundSetHelp {
    @Resource
    private FundSetModel fundSetModel;
    @Resource
    private ContractModel contractModel;
    @Resource
    private CustomerModel customerModel;


    public PeriodFilterPojo getPeriodFilter4Payment(String corpid, Long time, Long linkCustomerId, Long linkContractId, Long generationDate) throws XbbException {
        //不会为null，内部已经处理
        FundSetEntity setEntity = fundSetModel.getToRedis(corpid, XbbRefTypeEnum.CRM);
        Long modelType = setEntity.getSysLong10();
        Integer manageType = setEntity.getManageType();
        //初始化时默认置为无账期，且应收生成日期和预计回款日期一致
        PeriodFilterPojo periodFilterPojo = new PeriodFilterPojo(modelType, manageType, time);
        ModelTypeEnum modelTypeEnum = ModelTypeEnum.getByCode(modelType);
        switch (modelTypeEnum) {
            case PLAN:
                /*
                 * “手动创建应收”模式无需设置账期，格式置为“应收生成后”：应收生成后，不足1天按1天计算
                 * 应收生成时间为：当前时间
                 * 预计回款时间为：回款计划的预计回款时间
                 * 账期为：预计回款日期-应收生成日期
                 */
                Long now = DateTimeUtil.getInt();
                //应收生成后，不足1天按1天计算，所以用0点去计算天数
                Integer betweenDay = DateTimeUtil.betweenDays(Objects.isNull(generationDate) ? DateTimeUtil.getTodayInt() : DateTimeUtil.getTodayInt(generationDate), DateTimeUtil.getTodayInt(time));
                periodFilterPojo.setGenerationTime(now);
                periodFilterPojo.setEstimateTime(time);
                periodFilterPojo.setType(PeriodTypeEnum.DAY_AFTER.getCode());
                periodFilterPojo.setDay(betweenDay);
                break;
            case INVOICE:
            case OUTSTOCK:
                ManageTypeEnum manageTypeEnum = ManageTypeEnum.getByCode(manageType);
                if (Objects.isNull(manageTypeEnum) || Objects.equals(manageType, ManageTypeEnum.NONE.getCode())) {
                    return periodFilterPojo;
                }
                List<FundSetPeriodFilterPojo> periodFilter = JsonHelperUtil.parseArray(setEntity.getPeriodFilter(), FundSetPeriodFilterPojo.class);
                if (CollectionsUtil.isEmpty(periodFilter)) {
                    //为空则当做无账期
                    return periodFilterPojo;
                }
                switch (manageTypeEnum) {
                    case CORP:
                        //企业统一管理账期
                        setPeriodFilterByCorp(periodFilter, time, periodFilterPojo);
                        break;
                    case CUSTOMER:
                        //根据客户管理账期
                        setPeriodFilterByCustomer(periodFilter, time, periodFilterPojo, corpid, linkCustomerId);
                        break;
                    case CONTRACT:
                        //根据合同管理账期
                        setPeriodFilterByContract(periodFilter, time, periodFilterPojo, corpid, linkContractId);
                        break;
                    default:
                        //非“手动创建应收”模式的无账期，应收生成日期 = 预计回款日期
                        break;
                }
                break;
            default:
                break;
        }
        return periodFilterPojo;
    }

    public String getImportDefaultPeriod(String corpid, Long formId, JSONObject jsonObject) throws XbbException {
        //把资金设置相关信息从数据库读取出，再保存入缓存
        FundSetEntity setEntity = fundSetModel.getToRedis(corpid, XbbRefTypeEnum.CRM);
        if (Objects.isNull(setEntity)) {
            return "";
        }else {
            List<FundSetPeriodFilterPojo> periodFilter = JsonHelperUtil.parseArray(setEntity.getPeriodFilter(), FundSetPeriodFilterPojo.class);
            for (FundSetPeriodFilterPojo fundSetPeriodFilterPojo : periodFilter) {
                if (formId.equals(fundSetPeriodFilterPojo.getFormId())) {
                    String attr = fundSetPeriodFilterPojo.getAttr();
                    String value = jsonObject.getString(attr);
                    if (Objects.isNull(value)) {
                        break;
                    }
                    List<FundSetPeriodRulePojo> fundSetPeriodRulePojoList = fundSetPeriodFilterPojo.getRule();
                    for (FundSetPeriodRulePojo fundSetPeriodRulePojo : fundSetPeriodRulePojoList) {
                        if (value.equals(fundSetPeriodRulePojo.getCode())) {
                            Integer type = fundSetPeriodRulePojo.getType();
                            Integer month = fundSetPeriodRulePojo.getMonth();
                            Integer day = fundSetPeriodRulePojo.getDay();
                            JSONObject jsonObjectSen = new JSONObject();
                            jsonObjectSen.put(BasicConstant.TYPE, type);
                            jsonObjectSen.put(PeriodTypeEnum.MONTH.getAlias(), month);
                            jsonObjectSen.put(PeriodTypeEnum.DAY_AFTER.getAlias(), day);
                            return jsonObjectSen.toJSONString();
                        }
                    }
                }
            }
        }
        return "";
    }
    /**
     * 获取资金设置页左侧菜单
     * @param refType 所属模块，1：crm 2：jxc
     * @return java.util.List<com.xbongbong.pro.fundset.pojo.FundSetMenuPojo>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<FundSetMenuPojo> getFundSetMenu(Integer refType) {
        List<FundSetMenuPojo> menuList = new ArrayList<>();
        List<FundSetMenuEnum> setEnumList = FundSetMenuEnum.getEnumListByRefType(refType);
        for (FundSetMenuEnum cache : setEnumList) {
            FundSetMenuPojo fundSetMenuPojo = new FundSetMenuPojo(cache.getType(), cache.getName());
            menuList.add(fundSetMenuPojo);
        }
        return menuList;
    }

    /**
     * 计算预计回款日期
     * @param time 应收款的预计回款日期、发票的开票日期、出库的出库日期
     * @param fundSetPeriodRulePojo 账期条件
     * @return java.lang.Long
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public Long getEstimateTimeByFilter(Long time, FundSetPeriodRulePojo fundSetPeriodRulePojo) {
        Integer day = fundSetPeriodRulePojo.getDay();
        PeriodTypeEnum typeEnum = PeriodTypeEnum.getByCode(fundSetPeriodRulePojo.getType());
        Long estimateTime = time;
        switch (typeEnum) {
            case WEEK:
                //每周几
                Integer weekNum = DateTimeUtil.getWeekInSomeday4Normal(time);
                Integer count = day - weekNum;
                estimateTime = time + count * TimeConstant.SECONDS_PER_DAY;
                if (count < 0) {
                    estimateTime += TimeConstant.SECONDS_PER_WEEK;
                }
                break;
            case MONTH:
                //每月几日，超过当月最大天数则用月末
                estimateTime = DateTimeUtil.getIntTimeOfDay(time, day);
                if (estimateTime < time) {
                    //算出的时间比time小，则获取下一个月的day日 同时同分同秒
                    estimateTime = DateTimeUtil.getIntTimeOfDay(DateTimeUtil.getNextTimeOfMonthInSomeday(time), day);
                }
                break;
            case YEAR:
                Integer month = fundSetPeriodRulePojo.getMonth();
                //几月几日
                estimateTime = DateTimeUtil.getIntTimeOfMonthDay(time, month, day);
                if (estimateTime < time) {
                    //算出的时间比time小，则获取下一年的month月day日 同时同分同秒
                    estimateTime = DateTimeUtil.getIntTimeOfMonthDay(DateTimeUtil.getNextTimeOfYearInSomeday(time), month, day);
                }
                break;
            case DAY_AFTER:
                estimateTime = time + day * TimeConstant.SECONDS_PER_DAY;
                break;
            default:
                break;
        }
        return estimateTime;
    }

    /**
     * 格式话应收账期
     * type值含义（1）1:每周 （2）2:每月 （3）3：每年 （4）4:应收账期后
     * @param receivablePeriod 应收账期json字符串
     * @return
     */
    public String getFormatReceivablePeriod(String receivablePeriod){
        StringBuilder stringBuilder = new StringBuilder();
        if (ObjectUtils.isEmpty(receivablePeriod)) {
            return null;
        }
        JSONObject jsonObject = JsonHelperUtil.parseObject(receivablePeriod);
        Integer type = (Integer) jsonObject.get("type");
        Integer day = (Integer) jsonObject.get("day");
        if (Objects.equals(PeriodTypeEnum.WEEK.getCode(), type)) {
            //代表是每周
            stringBuilder.append(I18nMessageUtil.getMessage(FundSetConstant.EVERY_WEEK)).append(I18nMessageUtil.getMessage(FundSetConstant.WEEK)).append(DayEnum.getByNumberStr(day));
        } else if (Objects.equals(PeriodTypeEnum.MONTH.getCode(), type)){
            //代表是每月
            stringBuilder.append(I18nMessageUtil.getMessage(FundSetConstant.EVERY_MONTH)).append(day).append(I18nMessageUtil.getMessage(FundSetConstant.NUMBER));
        } else if (Objects.equals(PeriodTypeEnum.YEAR.getCode(), type)) {
            Integer month = (Integer) jsonObject.get("month");
            //代表是每年
            stringBuilder.append(I18nMessageUtil.getMessage(FundSetConstant.EVERY_YEAR)).append(month).append(I18nMessageUtil.getMessage(FundSetConstant.MONTH)).append(day).append(I18nMessageUtil.getMessage(FundSetConstant.NUMBER));
        } else if (Objects.equals(PeriodTypeEnum.DAY_AFTER.getCode(), type)) {
            //代表是应收生成后
            stringBuilder.append(I18nMessageUtil.getMessage(FundSetConstant.RECEIVABLE_GENERATION)).append(day).append(I18nMessageUtil.getMessage(FundSetConstant.DAY));
        }
        return stringBuilder.toString();
    }
    /**
     * 判断当前数据是否为发票模式产生的数据，或者当前是否为开票模式
     * @param dataModelType 数据的模式，即哪种模式产生的数据
     * @param nowMmodelType 当前启用的应收规则模式
     * @return boolean true则代表当前数据是发票模式产生的数据，或者当前是开票模式
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public boolean checkInvoiceModelFlag(Long dataModelType, Long nowMmodelType) {
        return Objects.equals(dataModelType, ModelTypeEnum.INVOICE.getCode()) || Objects.equals(nowMmodelType, ModelTypeEnum.INVOICE.getCode());
    }

    /**
     * 资金设置切换应收规则、模板初始化，及模板编辑时处理字段解释用pojo
     * @param editFlag 编辑表单解释标识。当编辑模板时，某些特定字段不能强制开启，因为用户可能自己主动关闭。比如在非开票模式下发票的关联回款类型，如果主动关闭，不应该强制给它开启
     * @param modelTypeEnum 应收规则模式枚举
     * @param manageType 管理方式
     * @return com.xbongbong.pro.fundset.pojo.FundSetHandleExplainPojo
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public FundSetHandleExplainPojo setFundSetHandleExplainPojo(boolean editFlag, ModelTypeEnum modelTypeEnum, Integer manageType) {
        //各业务要处理的字段，后续如有其它字段要处理，则在此ExplainOpenCloseAttrPojo内增加和初始化
        ExplainOpenCloseAttrPojo explainOpenCloseAttrPojo = new ExplainOpenCloseAttrPojo();
        /**
         * 要开启/关闭字段的业务
         * key：businessType
         * value：要开启/关闭的字段-开启/关闭后是否能再关闭/开启isForbidden值(当该值为0时，关闭的字段不能再启用、启用的字段不能再关闭)
         */
        Map<Integer, Map<String, Integer>> openBusinessMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Integer, Map<String, Integer>> closeBusinessMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        ManageTypeEnum manageTypeEnum = ManageTypeEnum.getByCode(manageType);
        switch (modelTypeEnum) {
            case INVOICE:
                // 关闭（开启着的）【销项发票】的关联回款类型、回款单、应收款字段
                // 关闭（开启着的）【回款单/应收款】的开票金额字段
                // 关闭（开启着的）【合同/订货单】的应收款字段
                closeBusinessMap.put(XbbRefTypeEnum.INVOICE.getCode(), explainOpenCloseAttrPojo.getCloseInvoiceAttrMap());
                closeBusinessMap.put(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainOpenCloseAttrPojo.getClosePaymentSheetAttrMap());
                closeBusinessMap.put(XbbRefTypeEnum.PAYMENT.getCode(), explainOpenCloseAttrPojo.getClosePaymentAttrMap());
                closeBusinessMap.put(XbbRefTypeEnum.CONTRACT.getCode(), explainOpenCloseAttrPojo.getCloseContractAttrMap());
                // 按照管理方式控制客户/合同的应收账期
                setBusinessTypeListByManageType(manageTypeEnum, openBusinessMap, closeBusinessMap, explainOpenCloseAttrPojo);
                break;
            case OUTSTOCK:
                // 开启（关闭着的）【销项发票】的关联回款类型、回款单、应收款字段
                // 开启（关闭着的）【回款单/应收款】的开票金额字段
                // 关闭（开启着的）【合同/订货单】的应收款字段
                if (!editFlag) {
                    //编辑模板时，如果这些字段关闭着，没必要给它们强制开启
                    openBusinessMap.put(XbbRefTypeEnum.INVOICE.getCode(), explainOpenCloseAttrPojo.getOpenInvoiceAttrMap());
                    openBusinessMap.put(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainOpenCloseAttrPojo.getOpenPaymentSheetAttrMap());
                    openBusinessMap.put(XbbRefTypeEnum.PAYMENT.getCode(), explainOpenCloseAttrPojo.getOpenPaymentAttrMap());
                }
                closeBusinessMap.put(XbbRefTypeEnum.CONTRACT.getCode(), explainOpenCloseAttrPojo.getCloseContractAttrMap());
                // 按照管理方式控制客户/合同的应收账期
                setBusinessTypeListByManageType(manageTypeEnum, openBusinessMap, closeBusinessMap, explainOpenCloseAttrPojo);
                break;
            default:
                //未设置模式，统一按照“手动创建应收”模式
                // 开启（关闭着的）【销项发票】的关联回款类型、回款单、应收款字段
                // 开启（关闭着的）【回款单/应收款】的开票金额字段
                // 开启（关闭着的）【合同/订货单】的应收款字段
                if (!editFlag) {
                    //编辑模板时，如果这些字段关闭着，没必要给它们强制开启
                    openBusinessMap.put(XbbRefTypeEnum.INVOICE.getCode(), explainOpenCloseAttrPojo.getOpenInvoiceAttrMap());
                    openBusinessMap.put(XbbRefTypeEnum.PAYMENT.getCode(), explainOpenCloseAttrPojo.getOpenPaymentAttrMap());
                    openBusinessMap.put(XbbRefTypeEnum.PAYMENT_SHEET.getCode(), explainOpenCloseAttrPojo.getOpenPaymentSheetAttrMap());
                    openBusinessMap.put(XbbRefTypeEnum.CONTRACT.getCode(), explainOpenCloseAttrPojo.getOpenContractAttrMap());
                }
                // 关闭（开启着的）【客户/合同】的应收账期
                closeBusinessMap.put(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), explainOpenCloseAttrPojo.getManageCloseCustomerAttrMap());
                closeBusinessMap.put(XbbRefTypeEnum.CONTRACT.getCode(), explainOpenCloseAttrPojo.getManageCloseContractAttrMap());
                break;
        }
        return new FundSetHandleExplainPojo(openBusinessMap, closeBusinessMap);
    }

    /**
     * 切换应收规则时，要处理explain的业务枚举
     * @return java.util.List<java.lang.Integer>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<Integer> getSpecialBusinessList() {
        return Arrays.asList(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), XbbRefTypeEnum.CONTRACT.getCode(), XbbRefTypeEnum.INVOICE.getCode(), XbbRefTypeEnum.PAYMENT.getCode(), XbbRefTypeEnum.PAYMENT_SHEET.getCode());
    }

    /**
     * 主要处理：客户、合同的应收账期字段；销项发票的关联回款类型、回款单、应收款字段；回款单、应收款的开票金额字段
     * （1）按照资金设置的模式、管理方式
     *      ①“手动创建应收”模式：
     *          开启（关闭着的）【销项发票】的关联回款类型、回款单、应收款字段，用户可以自己关闭
     *          开启（关闭着的）【回款单/应收款】的开票金额字段，用户可以自己关闭
     *          关闭（开启着的）【客户/合同】的应收账期，且不允许启用；
     *      ②开票模式
     *          关闭（开启着的）【销项发票】的关联回款类型、回款单、应收款字段，且不允许启用
     *          关闭（开启着的）【回款单/应收款】的开票金额字段，且不允许启用
     *          关闭（开启着的）【合同/订货单】的应收款字段，且不允许启用
     *          按照管理方式控制客户/合同的应收账期：
     *              按照客户管理账期，开启（关闭着的）【客户】的应收账期，不允许关闭；关闭【合同】应收账期字段，且合同账期字段不允许启用
     *              按照合同管理账期，开启（关闭着的）【合同】的应收账期，不允许关闭；关闭【客户】应收账期字段，且合同账期字段不允许启用
     *              其他管理方式，都关闭
     *      ③出库模式
     *          开启（关闭着的）【销项发票】的关联回款类型、回款单、应收款字段，用户可以自己关闭
     *          开启（关闭着的）【回款单/应收款】的开票金额字段，用户可以自己关闭
     *          关闭（开启着的）【合同/订货单】的应收款字段，且不允许启用
     *          按照管理方式控制客户/合同的应收账期：
     *              按照客户管理账期，开启（关闭着的）【客户】的应收账期，不允许关闭；关闭【合同】应收账期字段，且合同账期字段不允许启用
     *              按照合同管理账期，开启（关闭着的）【合同】的应收账期，不允许关闭；关闭【客户】应收账期字段，且合同账期字段不允许启用
     *              其他管理方式，都关闭
     *
     * （2）字段关闭/开启后的操作权限
     *      a.客户/合同的应收账期字段：开启/关闭后，不允许关闭/启用
     *      b.销项发票的关联回款类型、回款单、应收款字段：开启后允许关闭，关闭后不允许启用
     *      c.回款单/应收款的开票金额字段：开启后允许关闭，关闭后不允许启用
     * @param editFlag 编辑表单解释标识。当编辑模板时，某些特定字段不能强制开启，因为用户可能自己主动关闭。比如在非开票模式下发票的关联回款类型，如果主动关闭，不应该强制给它开启
     * @param modelTypeEnum 应收规则模式枚举
     * @param manageType 管理方式
     * @param formExplainList 解释list
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<PaasFormExplainEntity> handleSpecialExplain(boolean editFlag, ModelTypeEnum modelTypeEnum, Integer manageType, List<PaasFormExplainEntity> formExplainList){
        //各业务要处理的字段，后续如有其它字段要处理，则在此ExplainOpenCloseAttrPojo内增加和初始化
        FundSetHandleExplainPojo fundSetHandleExplainPojo = setFundSetHandleExplainPojo(editFlag, modelTypeEnum, manageType);
        Map<Integer, Map<String, Integer>> openBusinessMap = fundSetHandleExplainPojo.getOpenBusinessMap();
        Map<Integer, Map<String, Integer>> closeBusinessMap = fundSetHandleExplainPojo.getCloseBusinessMap();
        List<PaasFormExplainEntity> updateList = new ArrayList<>();
        for (PaasFormExplainEntity explainEntity : formExplainList) {
            //外部调用的地方已经判断是否为要处理的业务类型
            Integer businessType = explainEntity.getBusinessType();
            Map<String, Integer> openAttrMap = openBusinessMap.get(businessType);
            Map<String, Integer> closeAttrMap = closeBusinessMap.get(businessType);
            boolean emptyOpen = CollectionUtils.isEmpty(openAttrMap);
            boolean emptyClose = CollectionUtils.isEmpty(closeAttrMap);
            if (emptyOpen && emptyClose) {
                //一个业务的字段要么开启，要么关闭，要么不处理
                continue;
            }
            List<FieldAttrEntity> explainList = JSONArray.parseArray(explainEntity.getExplains(), FieldAttrEntity.class);
            boolean flag = false;
            for (FieldAttrEntity fieldAttrEntity : explainList) {
                flag = handleFieldEntity(openAttrMap, closeAttrMap, emptyOpen, emptyClose, flag, fieldAttrEntity);
            }
            if (flag) {
                //把处理过的explain重置到explainEntity
                explainEntity.setExplains(JSONObject.toJSONString(explainList));
                updateList.add(explainEntity);
            }
        }
        return updateList;
    }

    /**
     * 处理字段解释
     * @param openAttrMap 开启字段-字段isForbidden
     * @param closeAttrMap 关闭字段-字段isForbidden
     * @param emptyOpen openAttrMap是否为空
     * @param emptyClose closeAttrMap是否为空
     * @param flag 是否更新标记
     * @param fieldAttrEntity 字段实体
     * @return boolean
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public boolean handleFieldEntity(Map<String, Integer> openAttrMap, Map<String, Integer> closeAttrMap, boolean emptyOpen, boolean emptyClose, boolean flag, FieldAttrEntity fieldAttrEntity) {
        //业务要开启字段(emptyOpen=false)，且该字段attr要开启(isForbiddenOpen不为null)，则判断原来是否为关闭（已经开启不用重复开启）
        if (!emptyOpen) {
            Integer isForbiddenOpen = openAttrMap.get(fieldAttrEntity.getAttr());
            if (Objects.nonNull(isForbiddenOpen) && Objects.equals(fieldAttrEntity.getIsOpen(), BasicConstant.ZERO)) {
                fieldAttrEntity.setIsOpen(BasicConstant.ONE);
                fieldAttrEntity.setIsForbidden(isForbiddenOpen);
                flag = true;
                return flag;
            }
        }
        if (!emptyClose) {
            //一个字段要么开启，要么关闭，要么不处理
            //业务要关闭字段(emptyClose=false)，且该字段attr要关闭(isForbiddenOpen不为null)，则判断原来是否为开启（已经关闭不用重复关闭）
            Integer isForbiddenClose = closeAttrMap.get(fieldAttrEntity.getAttr());
            if (Objects.nonNull(isForbiddenClose) && Objects.equals(fieldAttrEntity.getIsOpen(), BasicConstant.ONE)) {
                fieldAttrEntity.setIsOpen(BasicConstant.ZERO);
                fieldAttrEntity.setIsForbidden(isForbiddenClose);
                flag = true;
                return flag;
            }
        }
        return flag;
    }

    /**
     * 根据管理方式，设置客户、合同应收账期字段的开启、关闭情况
     * @param manageTypeEnum 管理方式
     * @param openAttrMap 塞入key-value为：businessType - 该业务要开启的字段map
     * @param closeAttrMap 塞入key-value为：businessType - 该业务要关闭的字段map
     * @param explainOpenCloseAttrPojo 各业务要开启、关闭的字段pojo（视后续产品迭代情况确定是否要扩展修改该pojo，或废弃不再使用该pojo）
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void setBusinessTypeListByManageType(ManageTypeEnum manageTypeEnum, Map<Integer, Map<String, Integer>> openAttrMap, Map<Integer, Map<String, Integer>> closeAttrMap, ExplainOpenCloseAttrPojo explainOpenCloseAttrPojo) {
        switch (manageTypeEnum) {
            case CUSTOMER:
                // 开启（关闭着的）【客户】的应收账期，不允许关闭；关闭【合同】应收账期字段，且合同账期字段不允许启用
                // 且要把切换应收规则时开启、关闭的字段汇总到openAttrMap、closeAttrMap内
                Map<String, Integer> customerMap = collectAttrMap(openAttrMap, XbbRefTypeEnum.CUSTOMER_MANAGEMENT, explainOpenCloseAttrPojo.getManageOpenCustomerAttrMap());
                Map<String, Integer> contractMap = collectAttrMap(closeAttrMap, XbbRefTypeEnum.CONTRACT, explainOpenCloseAttrPojo.getManageCloseContractAttrMap());
                openAttrMap.put(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), customerMap);
                closeAttrMap.put(XbbRefTypeEnum.CONTRACT.getCode(), contractMap);
                break;
            case CONTRACT:
                // 开启（关闭着的）【合同】的应收账期，不允许关闭；关闭【客户】应收账期字段，且合同账期字段不允许启用
                // 且要把切换应收规则时开启、关闭的字段汇总到openAttrMap、closeAttrMap内
                contractMap = collectAttrMap(openAttrMap, XbbRefTypeEnum.CONTRACT, explainOpenCloseAttrPojo.getManageOpenContractAttrMap());
                customerMap = collectAttrMap(closeAttrMap, XbbRefTypeEnum.CUSTOMER_MANAGEMENT, explainOpenCloseAttrPojo.getManageCloseCustomerAttrMap());
                openAttrMap.put(XbbRefTypeEnum.CONTRACT.getCode(), contractMap);
                closeAttrMap.put(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), customerMap);
                break;
            default:
                // 其他管理方式，都关闭。且要把切换应收规则时关闭的字段汇总到 closeAttrMap内
                customerMap = collectAttrMap(closeAttrMap, XbbRefTypeEnum.CUSTOMER_MANAGEMENT, explainOpenCloseAttrPojo.getManageCloseCustomerAttrMap());
                contractMap = collectAttrMap(closeAttrMap, XbbRefTypeEnum.CONTRACT, explainOpenCloseAttrPojo.getManageCloseContractAttrMap());
                closeAttrMap.put(XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode(), customerMap);
                closeAttrMap.put(XbbRefTypeEnum.CONTRACT.getCode(), contractMap);
                break;
        }
    }

    /**
     * 把切换模式，及更改管理方式要开启/关闭的客户、合同字段分别汇总到一个map内
     * @param openOrCloseAttrMap key-value为：businessType - 该业务要开启/关闭的字段map
     * @param xbbRefTypeEnum 业务类型
     * @param manageOpenOrCloseContractAttrMap 切换管理方式要开启/关闭的字段map
     * @return java.util.Map<java.lang.String,java.lang.Integer>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private Map<String, Integer> collectAttrMap(Map<Integer, Map<String, Integer>> openOrCloseAttrMap, XbbRefTypeEnum xbbRefTypeEnum, Map<String, Integer> manageOpenOrCloseContractAttrMap) {
        Map<String, Integer> map = openOrCloseAttrMap.get(xbbRefTypeEnum.getCode());
        if (Objects.isNull(map)) {
            map = manageOpenOrCloseContractAttrMap;
        } else {
            map.putAll(manageOpenOrCloseContractAttrMap);
        }
        return map;
    }

    /**
     * 企业统一管理账期：根据企业账期条件计算相关账期及预计回款日期等信息
     * @param periodFilter 资金设置内企业账期条件
     * @param time 回款计划的预计回款日期、发票的开票日期、出库的出库日期
     * @param periodFilterPojo 账期信息pojo：生成规则、管理方式、应收生成日期、预计回款日期等
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void setPeriodFilterByCorp(List<FundSetPeriodFilterPojo> periodFilter, Long time, PeriodFilterPojo periodFilterPojo) {
        for (FundSetPeriodFilterPojo pojo : periodFilter) {
            List<FundSetPeriodRulePojo> rule = pojo.getRule();
            if (CollectionsUtil.isEmpty(rule)) {
                continue;
            }
            FundSetPeriodRulePojo fundSetPeriodRulePojo = rule.get(0);
            //校验判断FundSetPeriodRulePojo是否设置详细账期规则
            if (checkPeriod(fundSetPeriodRulePojo)) {
                Long estimateTime = getEstimateTimeByFilter(time, fundSetPeriodRulePojo);
                periodFilterPojo.setDay(fundSetPeriodRulePojo.getDay());
                periodFilterPojo.setType(fundSetPeriodRulePojo.getType());
                periodFilterPojo.setMonth(fundSetPeriodRulePojo.getMonth());
                periodFilterPojo.setEstimateTime(estimateTime);
            }
        }
    }

    /**
     * 判断是否（因条件设置不全）有账期：true有，false无
     * 只判断时间相关设置，不判断是否设置字段条件
     * @param fundSetPeriodRulePojo
     * @return boolean
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public static boolean checkPeriod(FundSetPeriodRulePojo fundSetPeriodRulePojo){
        if (Objects.isNull(fundSetPeriodRulePojo)) {
            return false;
        }
        Integer day = fundSetPeriodRulePojo.getDay();
        PeriodTypeEnum typeEnum = PeriodTypeEnum.getByCode(fundSetPeriodRulePojo.getType());
        boolean flag = Objects.isNull(typeEnum) || Objects.equals(typeEnum, PeriodTypeEnum.NONE) || Objects.isNull(day) || day == 0;
        return !flag;
    }

    /**
     * 根据客户管理账期：根据账期条件计算相关账期及预计回款日期等信息
     * @param periodFilter 资金设置内客户账期条件
     * @param time 回款计划的预计回款日期、发票的开票日期、出库的出库日期
     * @param periodFilterPojo 账期信息pojo：生成规则、管理方式、应收生成日期、预计回款日期等
     * @param corpid 公司id
     * @param linkCustomerId 应收款关联的客户id
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void setPeriodFilterByCustomer(List<FundSetPeriodFilterPojo> periodFilter, Long time, PeriodFilterPojo periodFilterPojo, String corpid, Long linkCustomerId) {
        CustomerEntityExt customerEntityExt = customerModel.getByKey(linkCustomerId, corpid);
        if (Objects.isNull(customerEntityExt)) {
            //根据客户管理账期，结果关联客户实体不存在，则直接为无账期，后续不用判断：因为不管是读取客户的账期字段，还是根据资金设置，都需要用到关联客户实体（客户的formId、下拉选项值）
            return;
        }
        customerAndContractPeriodFilter(periodFilter, time, periodFilterPojo, customerEntityExt.getData(), CustomerManagementEnum.RECEIVABLE_PERIOD.getAttr(), customerEntityExt.getFormId());
    }

    /**
     * 客户、合同的预计回款日期计算
     * @param periodFilter 资金设置内客户/合同账期条件
     * @param time 回款计划的预计回款日期、发票的开票日期、出库的出库日期
     * @param periodFilterPojo 账期信息pojo：生成规则、管理方式、应收生成日期、预计回款日期等
     * @param data 客户/合同的data
     * @param periodAttr 客户/合同的【应收账期字段】attr
     * @param formId 客户/合同的模板id
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void customerAndContractPeriodFilter(List<FundSetPeriodFilterPojo> periodFilter, Long time, PeriodFilterPojo periodFilterPojo, JSONObject data, String periodAttr, Long formId) {
        //先读取关联客户/合同的账期
        JSONObject linkData = JsonHelperUtil.getJSONObject(data);
        String cusPeriodStr = linkData.getString(periodAttr);
        FundSetPeriodRulePojo cusFundSetPeriodRulePojo = JsonHelperUtil.parseObject(cusPeriodStr, FundSetPeriodRulePojo.class);
        if (checkPeriod(cusFundSetPeriodRulePojo)) {
            //客户/合同账期无问题，直接用客户/合同的账期字段计算预计回款日期
            BeanUtil.copyProperties(cusFundSetPeriodRulePojo, periodFilterPojo, true);
            Long estimateTime = getEstimateTimeByFilter(time, cusFundSetPeriodRulePojo);
            periodFilterPojo.setEstimateTime(estimateTime);
            return;
        }
        //如果关联客户/合同无账期（关联客户/合同本身就未填写账期字段的值、或值设置的不全），则读取资金设置的默认账期
        for (FundSetPeriodFilterPojo pojo : periodFilter) {
            //模板
            if (!Objects.equals(pojo.getFormId(), formId)) {
                continue;
            }
            List<FundSetPeriodRulePojo> rule = pojo.getRule();
            if (CollectionsUtil.isEmpty(rule)) {
                continue;
            }
            //考核字段（下拉单选or单选按钮）
            String attr = pojo.getAttr();
            //客户/合同数据内该考核字段的code值
            String code = linkData.getString(attr);
            if (StringUtil.isEmpty(code)) {
                //合同该字段的值为空
                continue;
            }
            for (FundSetPeriodRulePojo fundSetPeriodRulePojo : rule) {
                //客户/合同该字段的下拉选项值与规则内各个选项匹配，且设置详细账期规则
                if (Objects.equals(code, fundSetPeriodRulePojo.getCode()) && checkPeriod(fundSetPeriodRulePojo)) {
                    Long estimateTime = getEstimateTimeByFilter(time, fundSetPeriodRulePojo);
                    periodFilterPojo.setDay(fundSetPeriodRulePojo.getDay());
                    periodFilterPojo.setType(fundSetPeriodRulePojo.getType());
                    periodFilterPojo.setMonth(fundSetPeriodRulePojo.getMonth());
                    periodFilterPojo.setEstimateTime(estimateTime);
                    break;
                }
            }
        }
    }

    /**
     * 根据合同管理账期：根据账期条件计算相关账期及预计回款日期等信息
     * @param periodFilter 资金设置内合同账期条件
     * @param time 回款计划的预计回款日期、发票的开票日期、出库的出库日期
     * @param periodFilterPojo 账期信息pojo：生成规则、管理方式、应收生成日期、预计回款日期等
     * @param corpid 公司id
     * @param linkContractId 应收款关联的合同id
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private void setPeriodFilterByContract(List<FundSetPeriodFilterPojo> periodFilter, Long time, PeriodFilterPojo periodFilterPojo, String corpid, Long linkContractId) {
        ContractEntityExt contractEntityExt = contractModel.getByKey(linkContractId, corpid);
        if (Objects.isNull(contractEntityExt)) {
            //根据合同管理账期，结果关联合同实体不存在，则直接为无账期，后续不用判断：因为不管是读取合同的账期字段，还是根据资金设置，都需要用到关联合同实体（合同的formId、下拉选项值）
            return;
        }
        customerAndContractPeriodFilter(periodFilter, time, periodFilterPojo, contractEntityExt.getData(), ContractEnum.RECEIVABLE_PERIOD.getAttr(), contractEntityExt.getFormId());
    }
    /**
     * 新建客户、合同时，把当前模板的账期配置返回
     * @param corpid 公司id
     * @param businessType 业务类型，客户、合同
     * @param formId 表单id
     * @param explainList 客户/合同的解释list
     * @return com.xbongbong.pro.fundset.pojo.PeriodFilterPojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List<FieldAttrEntity> formatPeriod(String corpid, Integer businessType, Long formId, List<FieldAttrEntity> explainList) throws XbbException {
        //不会为null，内部已经处理
        FundSetEntity setEntity = fundSetModel.getToRedis(corpid, XbbRefTypeEnum.CRM);
        Long modelType = setEntity.getSysLong10();
        Integer manageType = setEntity.getManageType();
        boolean flag = Objects.equals(ModelTypeEnum.PLAN.getCode(), modelType) || Objects.equals(ManageTypeEnum.NONE.getCode(), manageType) || Objects.equals(ManageTypeEnum.CORP.getCode(), manageType);
        if (flag) {
            //“手动创建应收”模式，或管理方式为无账期/企业统一管理账期，则不用处理默认账期
            return explainList;
        }
        //如果是客户的，则需要资金设置的管理方式是根据客户管理账期；相应如果是合同，则需要管理方式为根据合同管理账期。否则，获取不到默认账期（因为是按照其他方式生成应收）
        String linkPeriod = "";
        if (Objects.equals(businessType, XbbRefTypeEnum.CUSTOMER_MANAGEMENT.getCode())) {
            if (Objects.equals(ManageTypeEnum.CONTRACT.getCode(), manageType)) {
                //根据合同管理账期，不用处理客户的账期字段
                return explainList;
            }
            linkPeriod = CustomerManagementEnum.RECEIVABLE_PERIOD.getAttr();
        } else if (Objects.equals(businessType, XbbRefTypeEnum.CONTRACT.getCode())) {
            if (Objects.equals(ManageTypeEnum.CUSTOMER.getCode(), manageType)) {
                //根据客户管理账期，不用处理合同的账期字段
                return explainList;
            }
            linkPeriod = ContractEnum.RECEIVABLE_PERIOD.getAttr();
        }
        List<FundSetPeriodFilterPojo> periodFilter = JsonHelperUtil.parseArray(setEntity.getPeriodFilter(), FundSetPeriodFilterPojo.class);
        if (CollectionsUtil.isEmpty(periodFilter)) {
            //为空则当做无账期
            return explainList;
        }
        FundSetPeriodFilterPojo fundSetPeriodFilterPojo = null;
        for (FundSetPeriodFilterPojo setPeriodFilterPojo : periodFilter) {
            if (Objects.equals(setPeriodFilterPojo.getFormId(), formId)) {
                fundSetPeriodFilterPojo = setPeriodFilterPojo;
                break;
            }
        }
        List<FundSetPeriodRulePojo> rule = Objects.isNull(fundSetPeriodFilterPojo) ? null : fundSetPeriodFilterPojo.getRule();
        if (CollectionsUtil.isEmpty(rule)) {
            return explainList;
        }
        //下拉选项code - 账期设置
        Map<String, FundSetPeriodRulePojo> rulePojoMap = rule.stream().collect(Collectors.toMap(rulePojo -> rulePojo.getCode(), rulePojo -> rulePojo));
        String attr = fundSetPeriodFilterPojo.getAttr();
        for (FieldAttrEntity fieldAttrEntity : explainList) {
            //考核字段
            if (Objects.equals(fieldAttrEntity.getAttr(), attr)) {
                List<ItemPoJo> items = fieldAttrEntity.getItems();
                if (org.apache.commons.collections4.CollectionUtils.isEmpty(items)) {
                    break;
                }
                for (ItemPoJo item : items) {
                    FundSetPeriodRulePojo rulePojo = rulePojoMap.getOrDefault(item.getValue().toString(), new FundSetPeriodRulePojo());
                    ReceivablePeriodPojo receivablePeriodPojo = new ReceivablePeriodPojo(linkPeriod, rulePojo.getType(), rulePojo.getMonth(), rulePojo.getDay());
                    item.setReceivablePeriod(receivablePeriodPojo);
                }
                break;
            }
        }
        return explainList;
    }

    /**
     * 关联合同应收账期不可见时生成默认应收账期
     *
     * @param corpid
     * @param
     * @param formId
     * @param
     * @author 吕瑞娟
     * @return
     */
    public String getDefaultFormatPeriod(String corpid, Long formId, JSONObject jsonObject) throws XbbException {
        //不会为null，内部已经处理
        FundSetEntity setEntity = fundSetModel.getToRedis(corpid, XbbRefTypeEnum.CRM);
        JSONObject jsonObjectSen = new JSONObject();
        if (Objects.isNull(setEntity)){
            return "";
        }else{
            Integer manageType = setEntity.getManageType();
            // 根据合同订单或者客户管理账期
            boolean flag = Objects.equals(ManageTypeEnum.CONTRACT.getCode(), manageType) || Objects.equals(ManageTypeEnum.CUSTOMER.getCode(), manageType);
            if (flag) {
                List<FundSetPeriodFilterPojo> periodFilter = JsonHelperUtil.parseArray(setEntity.getPeriodFilter(), FundSetPeriodFilterPojo.class);
                if (CollectionsUtil.isEmpty(periodFilter)) {
                    //为空则当做无账期
                    return "";
                } else {
                    for (FundSetPeriodFilterPojo fundSetPeriodFilterPojo : periodFilter) {
                        if (Objects.equals(formId, fundSetPeriodFilterPojo.getFormId())) {
                            String linkPeriodValue = jsonObject.getString(fundSetPeriodFilterPojo.getAttr());
                            if (Objects.isNull(linkPeriodValue)){
                                break;
                            }
                            List<FundSetPeriodRulePojo> ruleList = fundSetPeriodFilterPojo.getRule();
                            for (FundSetPeriodRulePojo fundSetPeriodRulePojo : ruleList){
                                //获得应收帐期得关联数据值，并且赋值给应收帐期
                                if (Objects.equals(linkPeriodValue, fundSetPeriodRulePojo.getCode())){
                                    Integer type = fundSetPeriodRulePojo.getType();
                                    Integer month = fundSetPeriodRulePojo.getMonth();
                                    Integer day = fundSetPeriodRulePojo.getDay();
                                    jsonObjectSen.put(BasicConstant.TYPE, type);
                                    jsonObjectSen.put(PeriodTypeEnum.MONTH.getAlias(), month);
                                    jsonObjectSen.put(PeriodTypeEnum.DAY_AFTER.getAlias(), day);
                                    return jsonObjectSen.toJSONString();
                                }
                            }
                        }
                    }
                }
                return "";
            }
        }
        return "";
    }
}
